{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于用户的协同过滤"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#导入相应的模块\n",
    "import pickle\n",
    "import numpy as np\n",
    "import scipy.io as sio\n",
    "import scipy.sparse as ss\n",
    "from numpy.random import random\n",
    "from collections import defaultdict\n",
    "from numpy import linalg as la"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "协同过滤基于之前数据整理实现，先导入之前生成的目标数据文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#读取已整理好数据文件\n",
    "userIndex = pickle.load(open(\"PE_userIndex.pkl\",\"rb\"))\n",
    "itemIndex = pickle.load(open(\"PE_eventIndex.pkl\",\"rb\"))\n",
    "n_users = len(userIndex)\n",
    "n_items = len(itemIndex)\n",
    "userItemScores = sio.mmread(\"PE_userEventScores\").todense()\n",
    "itemsForUser = pickle.load(open(\"PE_eventsForUser.pkl\",\"rb\"))\n",
    "usersForItem = pickle.load(open(\"PE_usersForEvent.pkl\",\"rb\"))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基于用户的协同过滤，先找出两个不同用户共同参与过的活动，根据不同用户对这些活动的打分计算这两个用户之间的相似度，相似度有很多指标可以表示，比如通过计算欧几里德距离、cosine夹角，相关系数等，由于数据集是稀疏矩阵，直接引用cosine相似度或者相关系数会导致指标的计算过程中分母等于0，所以此处用欧几里德距离倒数的大小来表示相似度的大小，为保证相似度的取值在0，1之间，指标作了修改。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#定义用户之间相似度的函数\n",
    "def simUserBased(user1, user2):\n",
    "    item_Cmn = []\n",
    "    for item in itemsForUser[user1]:\n",
    "        if item in itemsForUser[user2]:\n",
    "            item_Cmn.append(item) #得到用户1与用户2都打过分的活动\n",
    "    if len(item_Cmn)==0: \n",
    "        return 0 #如果2个用户没有共同参与的活动，返回0\n",
    "    score_user1 = np.array([userItemScores[user1,item] for i in item_Cmn]) #用户1对共同参与过的活动打分集合\n",
    "    score_user2 = np.array([userItemScores[user2,item] for i in item_Cmn]) #用户2对共同参与过的活动打分集合\n",
    "    sim_Between = eclSim(score_user1,score_user2) #根据上述的打分计算这两个用户之间的相似度\n",
    "    return sim_Between #返回相似度\n",
    "def eclSim(A,B): #定义求解相似度的函数\n",
    "    return 1.0/(1.0+la.norm(A-B)) #采用较为简单的欧几里德间距倒数的变形来表示相似度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#定义基于用户协同过滤的推荐系统\n",
    "def userRecSys(userId,itemId,Method=simUserBased): \n",
    "    u = userIndex[userId] #从整理的数据文件中根据用户地址获得用户序号\n",
    "    i = itemIndex[itemId] #从整理的数据文件中根据活动地址获得活动序号\n",
    "    sim_acc = 0 \n",
    "    score_acc = 0\n",
    "    for user in usersForItem[i]: \n",
    "        sim = Method(user,u) #根据相似度函数获得用户之间相似度\n",
    "        sim_acc+=sim\n",
    "        score_acc+=sim*userItemScores[user,i] #根据不同用户之间的相似度对该活动进行预估打分\n",
    "    if score_acc==0:\n",
    "        return 0\n",
    "    ans = score_acc/sim_acc\n",
    "    return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(1.0, '1528500344'),\n",
       " (1.0, '2683913641'),\n",
       " (1.0, '573886273'),\n",
       " (1.0, '1812117472'),\n",
       " (1.0, '738942203'),\n",
       " (1.0, '966971643'),\n",
       " (1.0, '2447943335'),\n",
       " (1.0, '1554701042'),\n",
       " (1.0, '1228296357'),\n",
       " (1.0, '4233837355'),\n",
       " (1.0, '2168553474'),\n",
       " (1.0, '2293472224'),\n",
       " (1.0, '1268088620'),\n",
       " (1.0, '740269154'),\n",
       " (1.0, '2479854230'),\n",
       " (1.0, '3481603259'),\n",
       " (1.0, '404595428'),\n",
       " (1.0, '2577118609'),\n",
       " (1.0, '1395574268'),\n",
       " (0.83856523310265696, '4125420656'),\n",
       " (0.77345908033901356, '383607907'),\n",
       " (0.76732698797896026, '3315040094'),\n",
       " (0.75, '929107951'),\n",
       " (0.72821658930742661, '152418051'),\n",
       " (0.68733899460051451, '3509100603'),\n",
       " (0.59970004649648956, '4232519602'),\n",
       " (0.54271624248245021, '3841472085'),\n",
       " (0.49638598615788149, '2790605371'),\n",
       " (0.42857142857142855, '2398192028'),\n",
       " (0.42264973081037427, '881346279'),\n",
       " (0.33401135624917039, '4203627753'),\n",
       " (0.25494023594129622, '3251813967'),\n",
       " (0.23586024596750449, '799782433'),\n",
       " (0.20919456267346884, '110357109'),\n",
       " (0.20147157210870201, '1501679119'),\n",
       " (0.14748742706434662, '675888033'),\n",
       " (0.094646875710564765, '899234866'),\n",
       " (0.066925447633060658, '1597380017'),\n",
       " (0.064305929481047477, '2428145712'),\n",
       " (0, '4043937476'),\n",
       " (0, '3854862695'),\n",
       " (0, '2116267579'),\n",
       " (0, '3771678348'),\n",
       " (0, '4223173683'),\n",
       " (0, '3832253163'),\n",
       " (0, '3726974662'),\n",
       " (0, '46008403'),\n",
       " (0, '2385597850'),\n",
       " (0, '2522843655'),\n",
       " (0, '3284996128'),\n",
       " (0, '1831967705'),\n",
       " (0, '548641030'),\n",
       " (0, '3810234126'),\n",
       " (0, '172445691'),\n",
       " (0, '2230413417'),\n",
       " (0, '1020936011'),\n",
       " (0, '2652552299'),\n",
       " (0, '2896575744'),\n",
       " (0, '1368913485'),\n",
       " (0, '2704757206')]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Res_s = [] #定义空列表\n",
    "for itemId in itemIndex: \n",
    "    res = userRecSys(\"4236494\",itemId) #同其他两个协同过滤一样，随机指定一个用户，如\"4236494\"，对其进行活动推荐\n",
    "    Res_s.append((res, itemId)) \n",
    "sorted(Res_s, key=lambda x: x[0],reverse=True)[:60] #向该用户推荐他可能最感兴趣的前60项活动"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "从推荐结果来看，对随机指定的同一个用户，svd模型协同过滤推荐的活动排序与其他两个的推荐并不相同。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
